/**
 * Copyright 2019-2021 Huawei Technologies Co., Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <securec.h>
#include <map>
#include "runtime/rt.h"
#include "attr_list.h"
#include "cce/common/offsetinfo.hpp"
namespace fe {
const uint32_t ATTRLIST_MAXSIZE = 8;
const uint32_t ATTRLIST_SIZE_TIMES = 2;

using attrInfoMap_t = std::map<uint32_t, OffsetInfo>;   /* <attrid, OffsetInfo> */
using attrInfoPair_t = std::pair<uint32_t, OffsetInfo> ; /* <attrid, OffsetInfo> */

#define CHECK(expr, errorcode) \
  ({                           \
    if (!(expr)) {             \
      return errorcode;        \
    }                          \
  })

#define CHECK_AND_RETURN(expr) \
  ({                           \
    if (!(expr)) {             \
      return *this;            \
    }                          \
  })

#define FREE_HOSTMEM_NORET(ptr, ret)        \
  {                                         \
    if (ptr == nullptr) {                   \
      ret = ATTR_ERROR_NULL_POINT;          \
    }                                       \
    if (rtFreeHost(ptr) != RT_ERROR_NONE) { \
      ret = ATTR_ERROR_FREE_FAIL;           \
    }                                       \
      ptr = nullptr;                        \
  }

struct AttrListPrivate
{
  void *buf;
  uint32_t maxSize;
  uint32_t size;
  attrInfoMap_t attrInfoMap_;
  explicit AttrListPrivate(uint32_t initLen) : buf(nullptr), maxSize(initLen), size(0) {}

  AttrListPrivate(const AttrListPrivate& in) = delete;// forbid use copy construct
  AttrListPrivate& operator = (const AttrListPrivate& in) = delete;// forbid use copy construct

  ~AttrListPrivate()
  {
    Clear();
  }

  void Clear()
  {
    size = 0;
    maxSize = 0;
    if (buf == nullptr) {
      return;
    }
    rtError_t reRet = rtFreeHost(buf);
    if (reRet == RT_ERROR_NONE) {
      buf = nullptr;
    }
  }
};

AttrList::AttrList()
{
  impl_ = nullptr;
  initLen_ = ATTRLIST_MAXSIZE;
}

AttrList::AttrList(uint32_t initLen)
{
  impl_ = nullptr;
  initLen_ = (initLen < ATTRLIST_MAXSIZE) ? ATTRLIST_MAXSIZE : initLen;
}

AttrList::~AttrList()
{
  if (impl_ != nullptr) {
    impl_->attrInfoMap_.erase(impl_->attrInfoMap_.begin(), impl_->attrInfoMap_.end());
    delete impl_;
    impl_ = nullptr;
  }
}

//lint -save -e1539
AttrList& AttrList::operator= (const AttrList& rhs)
{
  uint32_t ret = Init();
  CHECK_AND_RETURN((ATTR_SUCCESS == ret));
  if (&rhs == this) {
    return *this;
  }
  impl_->Clear();
  if (rhs.impl_ == nullptr) {
    impl_->attrInfoMap_.erase(impl_->attrInfoMap_.begin(), impl_->attrInfoMap_.end());
    impl_->maxSize = ATTRLIST_MAXSIZE;
    impl_->size = 0;
    return *this;
  }
  impl_->attrInfoMap_ = rhs.impl_->attrInfoMap_;
  impl_->size = rhs.impl_->size;

  if (rhs.impl_->maxSize != 0) {
    impl_->maxSize = rhs.impl_->maxSize;
    if (RT_ERROR_NONE != rtMallocHost(&(impl_->buf), impl_->maxSize)) {
      impl_->buf = nullptr;
      return *this;
    }
    if (impl_->buf != nullptr) {
      CHECK_AND_RETURN((0 == memcpy_s(impl_->buf, impl_->maxSize, rhs.impl_->buf, rhs.impl_->maxSize)));
    }
  }
  return *this;
}
//lint -restore
uint32_t AttrList::Add(uint32_t attrId, uint32_t attrLen, const void* attrValue)
{
  void *tempBuff = nullptr;
PROC
  EXEC(Init());
  ASSERT_JUDGE(ATTR_ERROR_BAD_PARAM, ((attrLen != 0) && (attrValue != nullptr)));

  uint32_t oldSize = impl_->maxSize;
  //if maxSize is too small, double it
  while (impl_->size + attrLen > impl_->maxSize) {
    if (impl_->maxSize < (UINT32_MAX / ATTRLIST_SIZE_TIMES)) {
      impl_->maxSize *= ATTRLIST_SIZE_TIMES;
    }
  }

  if (impl_->buf == nullptr) {
    EXEC_EX1(rtMallocHost(&(impl_->buf), impl_->maxSize), ATTR_ERROR_ALLOC_FAIL);
    JUDGE(ATTR_ERROR_NULL_POINT, (impl_->buf != nullptr));
  } else if (oldSize != impl_->maxSize) { //remalloc bigger memory
    EXEC_EX1(rtMallocHost(&tempBuff, impl_->maxSize), ATTR_ERROR_ALLOC_FAIL);
    JUDGE(ATTR_ERROR_NULL_POINT, (tempBuff != nullptr));
    EXEC_EX1((uint32_t)(memcpy_s(tempBuff, impl_->maxSize, impl_->buf, oldSize)), ATTR_ERROR_ALLOC_FAIL);
    EXEC_EX1(rtFreeHost(impl_->buf), ATTR_ERROR_FREE_FAIL);
    impl_->buf = tempBuff;
  }

  int ret = memcpy_s(reinterpret_cast<char *>(impl_->buf) + impl_->size, impl_->maxSize - impl_->size, \
                       attrValue, attrLen);
  if (ret != 0) {
    if (tempBuff != nullptr) {
      (void)rtFreeHost(tempBuff);
      impl_->buf = nullptr;
    }
    return ATTR_ERROR_BAD_PARAM;
  }
  OffsetInfo new_elem(impl_->size, attrLen);
  impl_->size += attrLen;
  impl_->attrInfoMap_[attrId] = new_elem; //lint !e737
  return ATTR_SUCCESS;
END_PROC
CATCH_ALL_ERROR
  uint32_t ret = ERROR_CODE();
  FREE_HOSTMEM_NORET(tempBuff, ret);
  (void)__catch_error_line_no;
  return ret;
END_CATCH_ERROR
}

uint32_t AttrList::Get(uint32_t attrId, uint32_t& attrLen, const void* &attr_value) const
{
  OffsetInfo elem;
  if (impl_ == nullptr) {
    attr_value = nullptr;
    return ATTR_ERROR_NOT_EXIST;
  }
  attrInfoMap_t::const_iterator it = impl_->attrInfoMap_.find(attrId);
  if (it == impl_->attrInfoMap_.end()) {
    return ATTR_ERROR_NOT_EXIST;
  }

  elem = it->second;
  attrLen = elem.len;

  if (impl_->buf != nullptr) {
    attr_value = reinterpret_cast<char*>(impl_->buf) + elem.start;
    return ATTR_SUCCESS;
  }
  return ATTR_ERROR_NULL_POINT;
}

uint32_t AttrList::Length() const
{
  return (impl_ == nullptr) ? uint32_t(0) : (uint32_t)impl_->attrInfoMap_.size();
}

uint32_t AttrList::Init()
{
  if (impl_ == nullptr) {
    impl_ = new(std::nothrow) AttrListPrivate(initLen_);
    CHECK((impl_ != nullptr), ATTR_ERROR_ALLOC_FAIL);
  }
  return ATTR_SUCCESS;
}
}
